#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

/*c语言不定参宏函数的使用*/

// fmt 用来格式控制，...表示不定参数，解析...需要使用__VA_ARGS__
// 但是如果我们只传了一个参数时，此时...为空，但是__VA_ARGS__依旧要进行解析，但此时并没有参数，就会报错, 因为多了一个逗号
// 例如：LOG("hahah") -> printf("[%s-%d]hahaha", __FILE__, __LINE__,);
// 所以，我们需要在__VA_ARGS__前面加上##, ##的作用就是在不定参数为空时，删除逗号

//#define LOG(fmt, ...) printf("[%s-%d]" fmt, __FILE__, __LINE__, ##__VA_ARGS__);
//
//int main()
//{
//    LOG("hahaha");
//    LOG("%s:%d\n", "test for 不定宏函数", 1);
//    return 0;
//}


// c 语言不定参函数的使用
// va_list, va_start, va_arg, va_end , 这些都是宏

void Print(int count, ...)
{
    va_list ap; // va_list用来管理可变参数列表, 类似于指针
    // 调用函数时，要对参数进行压栈，可变参数的压栈位置是不确定的，但确定参数的位置是明确的。
    // 所以，借助va_srart可以让ap指向确定参数的下一个位置，也就是可变参数的第一个位置
    va_start(ap, count); 
    for(int i = 0; i < count; i++)
    {
        // 知道了可变参数的第一个位置，再利用va_arg来获取指定类型长度个字节
        // 获取之后，va_arg会让ap移动到下一个位置处
        int num = va_arg(ap, int);
        printf("arg[%d]:%d\n", i, num);
    }
    // 使用完成后，要利用va_end将ap指针置空，以及完成一些清理工作
    va_end(ap);
}

// 借助c语言不定参函数，实现printf
void myPrintf(const char* fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    char* res;
    int ret = vasprintf(&res, fmt, ap); // vasprintf: 可以将可变参数按照格式输出到字符串中
    if(ret != -1)
    {
        printf("%s\n", res);
        free(res);
    }
    va_end(ap);
}

int main()
{
    Print(1,100);
    Print(2, 1, 2);
    Print(5, 1, 2, 3, 4, 5);


    printf("\n");

    myPrintf("%s-%d\n", "hahaah", 123);
    return 0;
}